package org.jsets.fastboot.oss.service.impl;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import org.jsets.fastboot.oss.model.entity.FileMeta;
import org.jsets.fastboot.oss.model.entity.Storage;
import org.jsets.fastboot.oss.service.IStorageEngineService;
import org.jsets.fastboot.util.IoUtils;
import org.jsets.fastboot.util.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import io.minio.BucketExistsArgs;
import io.minio.GetObjectArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.ObjectWriteResponse;
import io.minio.PutObjectArgs;
import io.minio.RemoveBucketArgs;
import io.minio.RemoveObjectArgs;
import io.minio.messages.Bucket;
import lombok.extern.slf4j.Slf4j;

/**
 * 
 * MINIO 文件系统
 * 
 * @author wangjie
 *
 */
@Slf4j
public class ClusterMinioStorageService implements IStorageEngineService {

	private final Storage storage;
	private List<String> invokers = Lists.newArrayList();
	private Map<String, MinioClient> clients = Maps.newHashMap();
	
	public ClusterMinioStorageService(Storage storage) {
		this.storage = storage;
	}
	
	@Override
	public void initialize() {
		for(String endpoint:StringUtils.split(this.storage.getAccessUrl())) {
			MinioClient client = MinioClient.builder()
					.endpoint(endpoint)
					.credentials(this.storage.getAccessKey(), this.storage.getSecretKey())
					.build();
			this.invokers.add(endpoint);
			this.clients.put(endpoint, client);
		}
	}
	
	public MinioClient doSelect() {
		String invoker = this.invokers.get(ThreadLocalRandom.current().nextInt(0));
		log.info("Cluster Minio Selected[{}]", invoker);
		return this.clients.get(invoker);
	}
	
	@Override
	public void makeBucket(String bucket) {
		BucketExistsArgs existsArgs = BucketExistsArgs.builder().bucket(bucket).build();
		MakeBucketArgs makeArgs = MakeBucketArgs.builder().bucket(bucket).build();
		try {
			boolean exist = this.doSelect().bucketExists(existsArgs);
			if (!exist) {
				this.doSelect().makeBucket(makeArgs);
			}
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new RuntimeException(e.getMessage(), e);
		}
	}
	
	@Override
	public void removeBucket(String bucket) {
		RemoveBucketArgs removeBucketArgs = RemoveBucketArgs.builder().bucket(bucket).build();
		try {
			this.doSelect().removeBucket(removeBucketArgs);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	@Override
	public List<String> listBuckets() {
		List<String> result = Lists.newArrayList();
		try {
			List<Bucket> list = this.doSelect().listBuckets();
			list.forEach(t -> {
				result.add(t.name());
			});
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new RuntimeException(e.getMessage(), e);
		}
		return result;
	}

	@Override
	public void upload(FileMeta metadata, InputStream inputStream) {
		String bucket = metadata.getBucketName();
		String id = Long.toString(metadata.getId());
		try {
			PutObjectArgs uploadObject = PutObjectArgs
					.builder()
					.bucket(bucket)
					.object(id)
					.stream(inputStream, inputStream.available(), -1)
					.build();
			ObjectWriteResponse resp = this.doSelect().putObject(uploadObject);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			IoUtils.closeQuietly(inputStream);
		}
	}

	@Override
	public void upload(FileMeta metadata, byte[] data) {
		String bucket = metadata.getBucketName();
		String id = Long.toString(metadata.getId());
		InputStream stream = null;
		try {
			stream = new ByteArrayInputStream(data);
			PutObjectArgs uploadObject = PutObjectArgs
					.builder()
					.bucket(bucket)
					.object(id)
					.stream(stream, stream.available(), -1)
					.build();
			ObjectWriteResponse resp = this.doSelect().putObject(uploadObject);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			IoUtils.closeQuietly(stream);
		}
	}

	@Override
	public void remove(FileMeta metadata) {
		String bucket = metadata.getBucketName();
		String id = Long.toString(metadata.getId());
		try {
			this.doSelect().removeObject(
					RemoveObjectArgs.builder()
					.bucket(bucket)
					.object(id)
					.build());
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	@Override
	public InputStream download(FileMeta metadata) {
		String bucket = metadata.getBucketName();
		String id = Long.toString(metadata.getId());
		try {
			return this.doSelect().getObject(
					GetObjectArgs.builder()
					.bucket(bucket)
					.object(id)
					.build());
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new RuntimeException(e.getMessage(), e);
		}
	}

	@Override
	public void close() {
	}
	
}